import { Precision } from "../Precision";
import { XYZ } from "../coord/XYZ";

/**
 * @description 三维向量
 * @author xuld
 * @date 2024-11-08
 * @lastEditTime 2024-11-08 17:52:01
 * @lastEditors xuld
 */
export class Vector3 {
    /**
     * @description 内部坐标对象
     */
    private _coord: XYZ;

    /**
     * @description 创建三维向量
     * @param x - X坐标值，默认为0
     * @param y - Y坐标值，默认为0
     * @param z - Z坐标值，默认为0
     */
    constructor(x: number = 0, y: number = 0, z: number = 0) {
        this._coord = new XYZ(x, y, z);
    }

    /**
     * @description 获取X坐标值
     */
    public get x(): number {
        return this._coord.x;
    }

    /**
     * @description 设置X坐标值
     */
    public set x(value: number) {
        this._coord.x = value;
    }

    /**
     * @description 获取Y坐标值
     */
    public get y(): number {
        return this._coord.y;
    }

    /**
     * @description 设置Y坐标值
     */
    public set y(value: number) {
        this._coord.y = value;
    }

    /**
     * @description 获取Z坐标值
     */
    public get z(): number {
        return this._coord.z;
    }

    /**
     * @description 设置Z坐标值
     */
    public set z(value: number) {
        this._coord.z = value;
    }

    /**
     * @description 获取内部坐标对象
     */
    public get coord(): XYZ {
        return this._coord;
    }

    /**
     * @description 归一化向量，返回新向量
     * @param len - 归一化长度，默认为1
     * @returns 归一化后的新向量
     */
    public normalize(len: number = 1): Vector3 {
        const magnitude = this.magnitude;
        if (magnitude === 0) return new Vector3();
        const scale = len / magnitude;
        return new Vector3(
            this.x * scale,
            this.y * scale,
            this.z * scale
        );
    }

    /**
     * @description 对自身进行归一化
     */
    public normalizeSelf(): void {
        this._coord.normalizeSelf();
    }

    /**
     * @description 计算向量的模长
     * @returns 向量的模长
     */
    public get magnitude(): number {
        return this._coord.modulus();
    }

    /**
     * @description 计算向量模长的平方
     * @returns 向量模长的平方
     */
    public get squareMagnitude(): number {
        return this._coord.squareModulus();
    }

    /**
     * @description 向量加法，返回新向量
     * @param other - 要相加的向量
     * @returns 相加后的新向量
     */
    public add(other: Vector3): Vector3 {
        return new Vector3(
            this.x + other.x,
            this.y + other.y,
            this.z + other.z
        );
    }

    /**
     * @description 向量加法，结果存储在自身
     * @param other - 要相加的向量
     */
    public addSelf(other: Vector3): void {
        this._coord.x += other.x;
        this._coord.y += other.y;
        this._coord.z += other.z;
    }

    /**
     * @description 向量减法，返回新向量
     * @param other - 要相减的向量
     * @returns 相减后的新向量
     */
    public subtract(other: Vector3): Vector3 {
        let newVec = new Vector3(this.x - other.x, this.y - other.y, this.z - other.z);
        return newVec;
    }

    /**
     * @description 向量减法，结果存储在自身
     * @param other - 要相减的向量
     */
    public subtractSelf(other: Vector3): void {
        this._coord.x -= other.x;
        this._coord.y -= other.y;
        this._coord.z -= other.z;
    }

    /**
     * @description 向量乘以标量，返回新向量
     * @param scalar - 标量值
     * @returns 相乘后的新向量
     */
    public multiply(scalar: number): Vector3 {
        return new Vector3(
            this.x * scalar,
            this.y * scalar,
            this.z * scalar
        );
    }

    /**
     * @description 向量乘以标量，结果存储在自身
     * @param scalar - 标量值
     */
    public multiplySelf(scalar: number): void {
        this._coord.multiplySelf(scalar);
    }

    /**
     * @description 向量除以标量，返回新向量
     * @param scalar - 标量值
     * @returns 相除后的新向量
     * @throws 当除数为0时抛出错误
     */
    public divide(scalar: number): Vector3 {
        if (scalar === 0) {
            throw new Error("Vector3: Division by zero is not allowed");
        }
        return new Vector3(
            this.x / scalar,
            this.y / scalar,
            this.z / scalar
        );
    }

    /**
     * @description 向量除以标量，结果存储在自身
     * @param scalar - 标量值
     * @throws 当除数为0时抛出错误
     */
    public divideSelf(scalar: number): void {
        if (scalar === 0) {
            throw new Error("Vector3: Division by zero is not allowed");
        }
        this._coord.divideSelf(scalar);
    }

    /**
     * @description 计算两个向量的点积
     * @param other - 另一个向量
     * @returns 点积结果
     */
    public dot(other: Vector3): number {
        return this.x * other.x + this.y * other.y + this.z * other.z;
    }

    /**
     * @description 计算两个向量的叉积
     * @param other - 另一个向量
     * @returns 叉积结果向量
     */
    public cross(other: Vector3): Vector3 {
        const xResult = this.y * other.z - this.z * other.y;
        const yResult = this.z * other.x - this.x * other.z;
        const zResult = this.x * other.y - this.y * other.x;
        return new Vector3(xResult, yResult, zResult);
    }

    /**
     * @description 向量取反，返回新向量
     * @returns 取反后的新向量
     */
    public negate(): Vector3 {
        return new Vector3(
            -this.x,
            -this.y,
            -this.z
        );
    }

    /**
     * @description 向量取反，结果存储在自身
     */
    public negateSelf(): void {
        this._coord.negateSelf();
    }

    /**
     * @description 判断两个向量是否相等
     * @param other - 要比较的向量
     * @param tolerance - 容差值，默认为EPSILON4
     * @returns 是否相等
     */
    public equals(other: Vector3, tolerance: number = Precision.EPSILON4): boolean {
        return this._coord.equals(other.coord, tolerance);
    }

    /**
     * @description 从其他向量复制坐标值
     * @param other - 要复制的向量
     */
    public copyFrom(other: Vector3): void {
        this.x = other.x;
        this.y = other.y;
        this.z = other.z;
    }

    /**
     * @description 创建当前向量的副本
     * @returns 新的向量对象
     */
    public clone(): Vector3 {
        return new Vector3(this.x, this.y, this.z);
    }

    /**
     * @description 转换为字符串表示
     * @returns 格式化的向量字符串
     */
    public toString(): string {
        return `{"type": "Vector3", "x": ${this.x}, "y": ${this.y}, "z": ${this.z}}`;
    }
}